/*
Author: Praveen Kambam Sugavanam
Last Updated: 04/15/2012 02:34
*/
using namespace std;
#include <stdio.h>
#include <Kernel.h>
#include <SuperBlock.h>
#include <IndexNode.h>
#include <DirectoryEntry.h>
#include <stdlib.h>
#include <stdio.h>
#include <FileSystem.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/* fsck command */
void updateLinkCount(String n,int nlinkCount[]);

int main(int argc, char **argv)
{
  Kernel::initialize();
  
  if (argc != 2)
  {
    fprintf (stderr, "usage: fsck <filesystem> \n");
  }
  
  //open the file with read mode.
  char* fname = argv[1];
  int mems[100];int m=0;
  for (;m<100;m++)
    mems[m] = 0;
  m=0;
  RandomAccessFile * file = new RandomAccessFile(fname,"r");
  
  SuperBlock* sp = new SuperBlock();
  
  sp->read(file);
  
  printf("\nInode start : %d \n",(sp->getInodeBlockOffset()*SuperBlock::getBlockSize()));
  printf("Data start : %d \n",(sp->getDataBlockOffset()*SuperBlock::getBlockSize()));
  byte b[SuperBlock::getBlockSize()];
  
  file->readFully(b);
  
//  FileSystem fs = new FileSystem(fname,);
  FileSystem* fs = Kernel::openFileSystems[Kernel::ROOT_FILE_SYSTEM];
  //  FileSystem* fs = new FileSystem ("filesys.dat","r");
  
  int n_inodes = SuperBlock::getBlockSize() /64;
  n_inodes = n_inodes * (sp->getDataBlockOffset()-sp->getInodeBlockOffset());
  int i =0;
  int dblocks = ((sp->getBlocks()+1)/2)*SuperBlock::getBlockSize() /16;
  int nlinkCount[n_inodes];
  
  
  while (i < n_inodes)
  {
    nlinkCount[i++] = 0;
  }
  i=0;
  while (i < n_inodes)
  {
  IndexNode * ii = new IndexNode();
   fs->readIndexNode(ii,(short) i++);
   printf("Inode number: %d\n",i-1);
  printf ( "Inode ::nlinks: %d  \n%s\n",ii->getNlink(), ii->toString());
  
  }
  
  /*
  while (i < n_inodes)
  {
    IndexNode * inode = new IndexNode();
    
    inode->read(b,(sp->getInodeBlockOffset()*SuperBlock::getBlockSize())+(64*i));
  
    printf( "INODE :: \n %d \n %s \n \n",inode->getUid(),inode->toString());
    delete inode;
    i++;
  }
  */
  
  i = 0;
  /*int fd = open("filesys.dat",O_RDONLY);
  if(fd < 0)
    {
      // process->errno = ???
      return -1;
    }
    DirectoryEntry *directoryEntry = new DirectoryEntry();  
  while(true)
  {
      // read a directory entry
      int status = Kernel::readdir(fd, directoryEntry);
      if(status <= 0)
      {
        printf("Status ::: %d\n",status);
        // we got to the end of the directory, or 
        // encountered an error, so quit
        break;
      }
          printf("Inode of the de : %d \n",directoryEntry->getIno());
  
    printf(" Directory Entry \n %s \n \n",directoryEntry->toString());

      
  }*/
  /*
  while ( i < dblocks)
  {
    DirectoryEntry *de = new DirectoryEntry();
    de->read(b,(sp->getDataBlockOffset()*SuperBlock::getBlockSize())+(16*i));
  
    printf("Inode of the de : %d \n",de->getIno());
  
    printf(" Directory Entry \n %s \n \n",de->toString());
    delete de; i++;
  }
  */
  
  //--------------------------------------------
  FileDescriptor *fileDescriptor = 
    new FileDescriptor(fs, 0, O_RDONLY);
    String n = "/";
  int fd = Kernel::open(n,0);
  if(fd < 0)
    {
      // process->errno = ???
      return -1;
    }

  // create a buffer for reading directory entries
  DirectoryEntry *directoryEntry = new DirectoryEntry();

  int status = 0;
  int close_status;
  short indexNodeNumber = -1;
  // while there are more directory blocks to be read
  while(true)
    {
      // read a directory entry

      status = Kernel::readdir(fd, directoryEntry);
      //  printf("Inside while 2\n");
      if(status <= 0)
      {
        // we got to the end of the directory, or 
        // encountered an error, so quit
        break;
      }
   //    printf("Inode of the de : %d \n",directoryEntry->getIno());
       //if(strcmp(directoryEntry->getName(),".") && strcmp(directoryEntry->getName(),"..") )
         // nlinkCount[directoryEntry->getIno()]++;
      // printf(" Directory Entry \n %s \n \n",directoryEntry->toString());
   
    }
  updateLinkCount("/",nlinkCount);  
  i=0;
  int modified = 0;
  while (i < n_inodes)
  {
    IndexNode * ii = new IndexNode();
    fs->readIndexNode(ii,(short) i);
    printf("nlink count of inode %d is: %d\n",i,ii->getNlink());
    printf("number of directories linking to inode %d: %d\n\n",i,nlinkCount[i]); 
    char c;
    if (nlinkCount[i] != ii->getNlink())
    {
      printf("Do you want to repiar the error in nlink count (y/n)? DO YOU? \n");
      c = getchar();
    }
    if ( c=='y'){
    modified = 1;
    ii->setNlink(nlinkCount[i]);
    fs->writeIndexNode(ii,i);
    }
    i++;
  }
  if(modified)
    printf("------------------- AFTER MODIFICATION ----------------------\n");
  i=0;
  while (modified &&   (i < n_inodes))
  {
    IndexNode * ii = new IndexNode();
    fs->readIndexNode(ii,(short) i);
//    IndexNode * ii = new IndexNode();
 //   fs->readIndexNode(ii,(short) i);
    
    printf("nlink count of inode %d is: %d\n",i,ii->getNlink());
    printf("number of directories linking to inode %d: %d\n\n",i,nlinkCount[i]);
   
    i++;
  }
  //find("/");
  file->Close();
    


  Kernel::Exit(0);
}

void updateLinkCount(String n,int nlinkCount[])
{
  //String n = "/";
  int fd = Kernel::open(n,0);
  if(fd < 0)
    {
      // process->errno = ???
      return ;
    }

  // create a buffer for reading directory entries
  DirectoryEntry *directoryEntry = new DirectoryEntry();

  int status = 0;
  int close_status;
  short indexNodeNumber = -1;
  // while there are more directory blocks to be read
  while(true)
    {
      // read a directory entry

      status = Kernel::readdir(fd, directoryEntry);
          //  printf("Inside while 2\n");
      if(status <= 0)
      {
        // we got to the end of the directory, or 
        // encountered an error, so quit
        break;
      }
       printf("Inode of the de : %d \n",directoryEntry->getIno());
       printf("Directory Entry : %s \n\n",directoryEntry->toString());

       /* Stat *stat = new Stat();
      status = Kernel::stat(n, stat);
      if(status < 0)
	{
	 // Kernel::perror(PROGRAM_NAME);
	  Kernel::Exit(1);
	}
      // mask the file type from the mode
      short type = (short)(stat->getMode() &((short)0170000));*/
      if (strcmp(directoryEntry->getName(),".."))
       if(strcmp(directoryEntry->getName(),"."))
       {
         if (strcmp("/",n))
           printf("%s",n);  
         printf("/%s \n",directoryEntry->getName());
         nlinkCount[directoryEntry->getIno()]++;
       }
       
       if(strcmp(directoryEntry->getName(),".")&& strcmp(directoryEntry->getName(),".."))
	{
	//append the name to this name and call this recursively.
	char* appName = (char*)malloc(strlen(n) + strlen(directoryEntry->getName()) + 2);
	strcpy(appName, n);
	if (strcmp("/",n))
          strcat(appName, "/");

        strcat(appName, directoryEntry->getName());
        //printf("In here and %s\n",appName);
        updateLinkCount(appName,nlinkCount);
	}
	
    }

  
}
